package com.dfs.parent.gateway.config;

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

import java.time.Duration;
import java.util.UUID;

/**
 * @Description TODO
 * @Author dahong
 * @Date 2025/8/2 21:46
 */
@Slf4j
@Configuration
public class WebClientConfig {
    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {

        ConnectionProvider connectionProvider = ConnectionProvider.builder("webclient-pool")
                .maxConnections(500) // 设置最大连接数
                .maxIdleTime(Duration.ofSeconds(10))    // ✅ 连接最大空闲时间（替代 idleTimeOut）空闲 10 秒关闭连接
                .maxLifeTime(Duration.ofMinutes(5))     // 连接最大存活时间
                .pendingAcquireMaxCount(-1) // 没有等待获取连接的最大数量限制
                .evictInBackground(Duration.ofSeconds(120)) // 每隔 120 秒执行一次清理，清理掉空闲的连接
                .build();

        HttpClient httpClient = HttpClient.create(connectionProvider)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) //建连最多等 3 秒，防止卡死
                .doOnConnected(conn ->
                        conn.addHandlerLast(new ReadTimeoutHandler(3)) //读数据超时 3 秒
                                .addHandlerLast(new WriteTimeoutHandler(3))) //写数据超时 3 秒
                .keepAlive(true);//true：表示 TCP 连接在一次请求完成后不会立即关闭，而是保持打开，供后续请求复用

        return WebClient.builder()
                .filter(logRequest())
                .filter(logResponse())
                .filter(requestIdFilter())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .clientConnector(new ReactorClientHttpConnector(httpClient));
    }

    /**
     * 在请求被发送到服务器之前，记录请求的详细信息（方法、URL、头等）
     *
     * @return
     */
    private ExchangeFilterFunction logRequest() {
        return (clientRequest, next) -> {
            // 记录请求信息
            String requestLog = String.format(
                    "发送请求: %s %s, Headers: %s",
                    clientRequest.method(),
                    clientRequest.url(),
                    clientRequest.headers());
            log.debug(requestLog);
            // 继续处理请求
            return next.exchange(clientRequest);
        };
    }

    /**
     * 在收到服务器响应后，记录响应的状态码、头等信息
     *
     * @return
     */
    private ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            // 记录响应信息
            String responseLog = String.format(
                    "收到响应: 状态码=%d %s, Headers: %s",
                    clientResponse.rawStatusCode(),
                    clientResponse.statusCode().getReasonPhrase(),
                    clientResponse.headers());
            log.debug(responseLog);
            return Mono.just(clientResponse);
        });
    }

    // 请求ID过滤器
    private ExchangeFilterFunction requestIdFilter() {
        return (request, next) -> {
            String requestId = UUID.randomUUID().toString();
            //ClientRequest.from(request) 是 Spring WebFlux 中用于复制并修改 HTTP 请求的构建器模式方法，主要用于 WebClient 的请求拦截和修改场景
            return next.exchange(ClientRequest.from(request)
                    .header("X-Request-ID", requestId)
                    .build());
        };
    }
}
